package com.platform.core.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.google.common.base.Preconditions;
import com.platform.comm.service.impl.CommonBizImpl;
import com.platform.core.constants.InterfaceStatus;
import com.platform.core.repository.NcloudSysInterfaceRepository;
import com.querydsl.core.BooleanBuilder;
import com.platform.comm.dto.DtoMapper;
import com.platform.comm.enums.YnEnum;
import com.platform.comm.exceptions.ValidationException;
import com.platform.core.entity.NcloudSysInterfaceEntity;
import com.platform.core.entity.QNcloudSysInterfaceEntity;
import com.platform.core.entity.SysConfType;
import com.platform.core.security.auth.AuthenticationFacade;
import com.platform.core.service.NcloudSysInterfaceBiz;
import com.platform.core.service.SysConfTypeService;
import com.platform.dto.InterfaceDto;
import com.platform.dto.InterfaceTypeDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QSort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * Description: NcloudSysInterface-业务接口实现类
 *
 * @author Coder
 */
@Slf4j
@Service
public class NcloudSysInterfaceBizImpl extends CommonBizImpl<NcloudSysInterfaceRepository, NcloudSysInterfaceEntity, String> implements NcloudSysInterfaceBiz {

    private final QNcloudSysInterfaceEntity interfaceEntity = QNcloudSysInterfaceEntity.ncloudSysInterfaceEntity;
    private final DtoMapper mapper;
    private final SysConfTypeService sysConfTypeService;

    public NcloudSysInterfaceBizImpl(NcloudSysInterfaceRepository ncloudSysInterfaceRepository, DtoMapper mapper,
                                     SysConfTypeService sysConfTypeService) {
        super(ncloudSysInterfaceRepository);
        this.mapper = mapper;
        this.sysConfTypeService = sysConfTypeService;
    }

    @Override
    public Page<InterfaceDto> search(Map<String, String> param, Pageable pageable) {
        BooleanBuilder builder = new BooleanBuilder().and(interfaceEntity.delflag.contains(YnEnum.N.getCode()));
        String name = param.get("interfaceName");
        if (Strings.isNotBlank(name)) {
            builder.and(interfaceEntity.interfaceName.contains(name));
        }
        String type = param.get("typeId");
        if (Strings.isNotBlank(type)) {
            builder.and(interfaceEntity.typeId.eq(type));
        }
        String status = param.get("status");
        if (Strings.isNotBlank(status)) {
            builder.and(interfaceEntity.status.eq(status));
        }

        Page<NcloudSysInterfaceEntity> page = queryByPage(builder,
                PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), QSort.by(interfaceEntity.createDate.desc())));
        return page.map(NcloudSysInterfaceEntity::of);
    }

    private List<InterfaceDto> getInterfaceInfo(BooleanBuilder builder) {
        builder.and(interfaceEntity.status.eq(InterfaceStatus.PUBLISHED.name()));
        repository.count(builder);
        if (repository.count(builder) <= 0){
            return null;
        }
        return StreamSupport.stream(repository.findAll(builder).spliterator(), false)
                .map(NcloudSysInterfaceEntity::of)
                .collect(Collectors.toList());
    }

    @Override
    public InterfaceDto detail(String id) {
        InterfaceDto interfaceDto = NcloudSysInterfaceEntity.of(fetchById(id));
        SysConfType sysConfType = sysConfTypeService.fetchById(interfaceDto.getTypeId());
        if (Objects.nonNull(sysConfType)){
            interfaceDto.setTypeName(sysConfType.getTypeName());
        }
        return interfaceDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(InterfaceDto req) {
        check(req);
        if (repository.count(interfaceEntity.interfaceName.eq(req.getInterfaceName())) > 0) {
            throw new ValidationException("接口名称重复");
        }
        if (repository.count(interfaceEntity.interfaceUrl.eq(req.getInterfaceName())) > 0) {
            throw new ValidationException("接口url重复");
        }
        NcloudSysInterfaceEntity entity = mapper.map(req, NcloudSysInterfaceEntity.class);
        entity.setReqParam(JSONArray.toJSONString(req.getReqParam()));
        entity.setResParam(JSONArray.toJSONString(req.getResParam()));
        entity.setCreateBy(AuthenticationFacade.getUserId());
        entity.setCreateDate(LocalDateTime.now());
        entity.setDelflag(YnEnum.N.getCode());
        entity.setStatus(InterfaceStatus.DRAFT.name());
        entity.setIsUsed(YnEnum.N.name());
        entity.setIsClone(YnEnum.N.name());
        entity.setVersion(String.format("V%s", 1.0));
        saveEntity(entity);
    }

    private void check(InterfaceDto req) {
        Preconditions.checkArgument(Strings.isNotBlank(req.getInterfaceName()), "接口名称不能为空");
        Preconditions.checkArgument(Strings.isNotBlank(req.getInterfaceUrl()), "接口地址不能为空");
        Preconditions.checkArgument(Strings.isNotBlank(req.getReqType()), "接口请求类型不能为空");
        Preconditions.checkArgument(Strings.isNotBlank(req.getTypeId()), "接口类型不能为空");
//        todo 暂不校验请求参数和响应参数类型
        /*if (Objects.nonNull(req.getReqParam())) {
            Preconditions.checkArgument(Strings.isNotBlank(req.getReqParamType()), "接口请求参数存在时，请求参数类型不能为空");
        }

        if (Objects.nonNull(req.getResParam())) {
            Preconditions.checkArgument(Strings.isNotBlank(req.getResParamType()), "接口响应参数存在时，响应参数类型不能为空");
        }*/
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modify(InterfaceDto req) {
        check(req);
        NcloudSysInterfaceEntity entity = fetchById(req.getId());
        if (YnEnum.Y.name().equals(entity.getIsUsed())) {
            throw new ValidationException("接口正在使用中不能进行修改");
        }
        if (repository.count(interfaceEntity.interfaceName.eq(req.getInterfaceName())
                .and(interfaceEntity.version.eq(entity.getVersion())).and(interfaceEntity.id.notIn(req.getId()))) > 0) {
            throw new ValidationException("接口名称重复");
        }
        if (repository.count(interfaceEntity.interfaceUrl.eq(req.getInterfaceName())
                .and(interfaceEntity.id.notIn(req.getId()))) > 0) {
            throw new ValidationException("接口url重复");
        }
        mapper.map(req, entity);
        entity.setReqParam(JSONArray.toJSONString(req.getReqParam()));
        entity.setResParam(JSONArray.toJSONString(req.getResParam()));
        entity.setUpdateDate(LocalDateTime.now());
        entity.setUpdateBy(AuthenticationFacade.getUserId());
        entity.setStatus(InterfaceStatus.DRAFT.name());
        saveEntity(entity);
    }

    /**
     * 设置接口已被使用
     */
    @Override
    public void setIsUsed(String id) {
//        TODO 已发布且使用的接口不能进行修改 之后接口调用存储确定后修改
        NcloudSysInterfaceEntity interfaceEntity = repository.findById(id).orElseThrow(() -> new RuntimeException("接口不存在"));
        interfaceEntity.setIsUsed(YnEnum.Y.name());
        saveEntity(interfaceEntity);
    }

    /**
     * 克隆接口
     *
     * @param param 参数
     */
    @Override
    public void clone(Map<String, String> param) throws CloneNotSupportedException {
        Preconditions.checkArgument(StringUtils.isNotBlank(param.get("id")), "接口id不能为空");
        NcloudSysInterfaceEntity interfaceEntity = repository.findById(param.get("id")).orElseThrow(() -> new RuntimeException("接口不存在"));
        if (YnEnum.Y.name().equals(interfaceEntity.getIsClone())) {
            throw new ValidationException("接口不是最新版本，请在最新版本上克隆");
        }
        NcloudSysInterfaceEntity entity = interfaceEntity.cloneEntity();
        entity.setCreateBy(AuthenticationFacade.getUserId());
        entity.setInterfaceUrl(interfaceEntity.getInterfaceUrl() + "/copy");
        entity.setCreateDate(LocalDateTime.now());
        entity.setUpdateBy(null);
        entity.setUpdateDate(null);
        entity.setId(null);
        entity.setStatus(InterfaceStatus.DRAFT.name());
        entity.setIsUsed(YnEnum.N.name());
        entity.setVersion(String.format("V%s", Double.sum(Double.parseDouble(interfaceEntity.getVersion().substring(1)), 1.0)));
        saveEntity(entity);

        interfaceEntity.setIsClone(YnEnum.Y.name());
        saveEntity(interfaceEntity);
    }

    @Override
    public List<InterfaceTypeDto> searchPublish(String parentId) {
        List<InterfaceTypeDto> typeDtos = sysConfTypeService.treeByRouteCodeNotRecursion("clz-ops_classify", true, 2)
                .stream().map(InterfaceTypeDto::of).collect(Collectors.toList());
        return toInterface(typeDtos);
    }

    private List<InterfaceTypeDto> toInterface(List<InterfaceTypeDto> typeDtos) {
        return typeDtos.stream().peek(typeDto -> {
            typeDto.setInterfaces(getInterfaceInfo(new BooleanBuilder(interfaceEntity.typeId.eq(typeDto.getId()))));
            if (Objects.nonNull(typeDto.getChildren())) {
                List<InterfaceTypeDto> interfaceTypeDtos = toInterface(typeDto.getChildren().stream().map(InterfaceTypeDto::of)
                        .collect(Collectors.toList()));
                typeDto.setChildren(interfaceTypeDtos);
            }
        }).map(entity -> mapper.map(entity,InterfaceTypeDto.class)).collect(Collectors.toList());
    }

    @Override
    public void publish(String id) {
        NcloudSysInterfaceEntity entity = fetchById(id);
        entity.setUpdateDate(LocalDateTime.now());
        entity.setUpdateBy(AuthenticationFacade.getUserId());
        entity.setStatus(InterfaceStatus.PUBLISHED.name());
        saveEntity(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(String id) {
        NcloudSysInterfaceEntity entity = fetchById(id);
        if (YnEnum.Y.name().equals(entity.getIsUsed())) {
            throw new ValidationException("接口已使用不能进行删除");
        }
//        克隆的接口被删除，还原被克隆接口的版本，以便下次克隆


        entity.setUpdateBy(AuthenticationFacade.getUserId());
        entity.setUpdateDate(LocalDateTime.now());
        entity.setDelflag(YnEnum.Y.getCode());
        saveEntity(entity);
    }
}
